/**
 * Created by jiwei on 2018-04-12.
 */
/**
 * Copyright 2006-2008 xujiwei
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
//Download by http://www.bvbsoft.com
////////////////////////////////////////////////////////////////////////////////
// AJAXRequest
// Version: 0.8.10 Patch 3
// Author: xujiwei
// E-mail: vipxjw at 163 dot com
// Website: http://www.xujiwei.com/
//
// AJAXRequest Project Homepage:
//     http://ajax.xujiwei.com/
////////////////////////////////////////////////////////////////////////////////

/**
 * class AJAXRequest
 * @author      xujiwei
 * @copyright 2006-2008 xujiwei.
 * @constructor
 * @version     $Id: ajaxrequest.js 35 2008-12-27 05:32:21Z xujiwei $
 * @class               AJAXRequest
 * @param               {object}        [obj]                                   Init parameter object
 * @param               {string}        [obj.url=""]                    Url to request
 * @param               {string}        [obj.content=""]                Content to send
 * @param               {string}        [obj.method="GET"]              Request method, GET or POST
 * @param               {string}        [obj.charset]                   Charset of content
 * @param               {boolean}       [obj.async=true]                Async request, true or false
 * @param               {number}        [obj.timeout=3600000]   Request timeout in millisecond
 * @param               {function}      [obj.encode=encodeURIComponent]         Encoding method, default encodeURIComponent
 * @param               {function}      [obj.ontimeout]                 Timeout callback
 * @param               {function}      [obj.onrequeststart]    Request start callback
 * @param               {function}      [obj.onrequestend]              Request end callback
 * @param               {function}      [obj.oncomplete]                Request complete successful callback
 * @param               {function}      [obj.onexception]               Handle request exception callback
 * @property    {string}        url                                             Url to request
 * @property    {string}        content                                 Content to send
 * @property    {string}        method                                  Request method, GET or POST
 * @property    {string}        charset                                 Charset of content
 * @property    {boolean}       async                                   Async request, true or false
 * @property    {number}        timeout                                 Request timeout in millisecond
 * @property    {function}      encode                                  Encoding method
 * @property    {function}      ontimeout                               Timeout callback
 * @property    {function}      onrequeststart                  Request start callback
 * @property    {function}      onrequestend                    Request end callback
 * @property    {function}      oncomplete                              Request complete successful callback
 * @property    {function}      onexception                             Handle request exception callback
 * @example
 * // using default values to init AJAXRequest object.
 * var ajax1 = new AJAXRequest();
 * // using a parameter to init AJAXRequest object.
 * var ajax2 = new AJAXRequest({
 *      url: "getdata.asp",     // get data from getdata.asp
 *      method: "GET",          // GET method
 *      oncomplete: function(obj) {
 *              alert(obj.responesText);        // show data from getdata.asp
 * });
 */
function AJAXRequest(init) {
    var objPool = [], AJAX = this, _pool = AJAXRequest.__pool__ || (AJAXRequest.__pool__ = []);
    (function(obj) {
        // init xmlhttp pool, and some consts
        var emptyFun = function() { };

        // process inti parameter
        obj = obj ? obj : {};
        var prop = ['url', 'content', 'method', 'async', 'encode',      'timeout', 'ontimeout', 'onrequeststart', 'onrequestend', 'oncomplete', 'onexception'];
        var defs = ['',    '',        'GET',     true,    _GEC('UTF-8'), 3600000,   emptyFun,    emptyFun,         emptyFun,       emptyFun,     emptyFun];
        var pc = prop.length;
        while(pc--) {
            AJAX[prop[pc]] = getp(obj[prop[pc]], defs[pc]);
        }
        // get the first xmlhttp, if failed then return false
        if(!getXHR()) { return false; }
    })(init);

    // get param or its default
    function getp(p, d) { return p != undefined ? p : d; }

    // get XMLHttpRequest from pool
    function getXHR() {
        var xhr, _vers = [window.XMLHttpRequest, "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];
        for(var i = 0; i < _pool.length; i+=1) {
            if(_pool[i].readyState == 0 || _pool[i].readyState == 4) {
                return _pool[i];
            }
        }
        for(var i = 0; i < _vers.length; i+=1) {
            try {
                xhr = (_vers[i] && typeof(_vers[i]) == "function" ? new _vers[i] : new ActiveXObject(_vers[i]));
                break;
            } catch(e) {
                xhr = false;
                continue;
            }
        }
        if(!xhr) {
            throw 'Cannot init XMLHttpRequest object!';
            return false;
        }
        else {
            _pool[_pool.length] = xhr;
            return xhr;
        }
    }

    // get element by id
    function $(id) { return document.getElementById(id); }

    // convert anything to number
    function _N(d) { var n = d * 1; return(isNaN(n) ? 0 : n); }

    // convert anything to HtmlObject
    function _VO(v) { return (typeof(v) == "string" ? (v = $(v)) ? v : false : v); }

    // get an unique number
    function _GID(){return((new Date)*1);}

    // save update object
    function _SOP(id, ct) { objPool[id + ""] = ct; }

    // load update object
    function _LOP(id) { return(objPool[id + ""]); }

    // string replace function generator
    function _SRP(pre, reps, ps){
        return (function rep(str) {
            str = pre(str);
            for(var i = 0, n = reps.length; i < n; i+=1) {
                str = str.replace(reps[i], ps[i]);
            }
            return(str);
        });
    }

    // get encode method
    function _GEC(cs){
        if(cs.toUpperCase() == "UTF-8") {
            return(encodeURIComponent);
        }
        else {
            // replace sepcial chars: +
            return(_SRP(escape, [/\+/g], ["%2B"]));
        }
    }

    // set content to HtmlObject
    function _ST(obj, txt) {
        if(!obj.nodeName) {
            return;
        }
        var nn = "|" + obj.nodeName.toUpperCase() + "|";
        if("|INPUT|TEXTAREA|OPTION|".indexOf(nn) > -1) {
            obj.value = txt;
        }
        else {
            try {
                obj.innerHTML = txt;
            } catch(e) { };
        }
    }

    // generate a callback function
    function _CB(cb) {
        if(typeof(cb) == "function") {
            return cb;
        }
        else {
            cb = _VO(cb);
            if(cb) {
                return (function(obj) {
                    _ST(cb, obj.responseText);
                });
            }
            else {
                return AJAX.oncomplete;
            }
        }
    }

    // generate parameters
    // p  output parameters array
    // v  input parameter values
    // d  default values
    // f  extra process function
    function $GP(v, d, f) {
        var i = 0, p = [];
        while(i < v.length) {
            p[i] = v[i] ? (f[i] ? f[i](v[i]) : v[i]) : d[i];
            i+=1;
        }
        while(i < d.length) {
            p[i]=d[i];
            i+=1;
        }
        return p;
    }

    // send request
    function send() {
        var ct, ctf = false, xhr = getXHR();
        // process parameters
        var p = $GP(arguments,
            [AJAX.url, AJAX.content, AJAX.oncomplete, AJAX.method, AJAX.async, null],
            [null, null, _CB, null, null, null]);
        var url = p[0], content = p[1], callback = p[2], method = p[3], async = p[4], extra = p[5];

        // is use POST method?
        var isPost = method.toUpperCase() == "POST" ? true : false;

        // check if url exists
        if(!url) {
            throw 'url is null';
            return false;
        }

        // event callback argument
        var ev = {
            url: url,
            content: content,
            method: method,
            params: extra
        };

        // append a timestamp to the url and open XMLHttpRequest
        if(!isPost) {
            url += (url.indexOf("?") > -1 ? "&" : "?") + "timestamp=" + _GID();
        }
        xhr.open(method, url, async);

        // request start event
        AJAX.onrequeststart(ev);

        // POST method needs a sepcial Content-Type
        if(isPost) {
            xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
        }
        xhr.setRequestHeader("X-Request-With", "XMLHttpRequest");

        // set a timeout to cancel request when timeout
        ct = setTimeout(function() {
                ctf = true;
                xhr.abort();
            },
            AJAX.timeout);
        var rc = function() {
            if(ctf) {
                AJAX.ontimeout(ev);
                AJAX.onrequestend(ev);
            }
            else if(xhr.readyState == 4) {
                clearTimeout(ct);
                ev.status = xhr.status;
                try{
                    if(xhr.status == 200) {
                        callback(xhr, extra);
                    }
                    else {
                        AJAX.onexception(ev);
                    }
                }
                catch(e) {
                    AJAX.onexception(ev);
                }
                AJAX.onrequestend(ev);
            }
        }
        xhr.onreadystatechange = rc;
        if(isPost) { xhr.send(content); } else { xhr.send(""); }
        if(async == false) {
            rc();
        }
        return true;
    }

    /**
     * Set charset of content
     *
     * @param       {string}        charset charset, UTF-8 or GB2312, and etc..
     * @see AJAXRequest#charset
     * @see AJAXRequest#encode
     * @example
     * var ajax = new AJAXRequest();
     * ajax.setcharset("GB2312");
     */
    this.setcharset = function(cs) {
        AJAX.encode = _GEC(cs);
    }

    /**
     * Get data from sepcial url
     *
     * @param       {string}                                        [url]                   Url to request
     * @param       {function|object|string}        [oncomplete]    Successful callback, or Html object, or Html object's ID
     * @param       {object}                                        [extra]                 Extra data post to callback function
     * @returns     {boolean}                                                                       Is the request successfully sent
     * @example
     * var ajax = new AJAXRequest();
     * ajax.get("getdata.asp", function(obj) {
         *      alert(obj.responseText);        // show data from getdata.asp
         * });
     * ajax.get("getdata.asp", "txtData");  // show data from getdata.asp in html object "txtData"
     */
    this.get = function(url, callback, extra) {
        return send(url, '', callback, 'GET', AJAX.async, extra);;
    }

    /**
     * Update the sepcial object with data from request url
     *
     * @param       {function|object|string}        callback        oncomplete      Successful callback, or Html object, or Html object's ID
     * @param       {string}                                        url                     Url to request
     * @param       {number}                                        interval        Interval to update
     * @param       {number}                                        times           Total update times
     * @param       {object}                                        [extra]         Extra data post to callback function
     * @returns     {string}                                                                id of update request, use to stop the update
     * @see         AJAXRequest#stopupdate
     * @example
     * var ajax = new AJAXRequest();
     * ajax.update(function(obj) {
         *              alert(obj.responseText);
         *      },
     *      "getdata.asp",  // get data from getdata.asp
     *      1000,   // update per second
     *      3               // total update 3 times
     * );
     */
    this.update = function(callback, url, interval, times, extra) {
        interval = _N(interval);
        times = _N(times);
        if(interval < 1) {
            times = 1;
        }
        else if(times < 1) {
            times = Number.POSITIVE_INFINITY;
        }
        var sendfoo = function() {
            send(url, "", callback, "GET", AJAX.async, extra);
        };
        var updateid = _GID();
        var updatefoo = function(updateCount) {
            sendfoo();
            updateCount--;
            if(updateCount > 0) {
                _SOP(updateid, setTimeout(function(){
                    updatefoo(updateCount);
                }, interval));
            }
        }
        updatefoo(times);
        return updateid;
    }

    /**
     * Stop update an object
     *
     * @param       {string}        update_id       update id which return by update method
     * @see         AJAXRequest#update
     * @example
     * var ajax = new AJAXRequest();
     * var up = ajax.update("txtData", "getdata.asp");
     * ajax.stopupdate(up);
     */
    this.stopupdate = function(id) {
        clearTimeout(_LOP(id));
    }

    /**
     * Post data to the sepcial url
     *
     * @param       {string}                                        [url]                   Url to post data
     * @param       {string}                                        [content]               Data to post
     * @param       {function|object|string}        [oncomplete]    Successful callback, or Html object, or Html object's ID
     * @param       {object}                                        [extra]                 Extra data post to callback function
     * @returns     {boolean}                                                                       Is the request successfully sent
     * @see         AJAXRequest#postf
     * @example
     * var ajax = new AJAXRequest();
     * ajax.post("postdata.asp", "the data to post", function(){});
     */
    this.post = function(url, content, callback, extra) {
        return send(url, content, callback, "POST", AJAX.async, extra);
    }

    /**
     * Post the sepcial form to an url
     *
     * @param       {string|object}                         formObject              The form object or its ID
     * @param       {function|object|String}        [oncomplete]    Successful callback, or Html object, or Html object's ID
     * @param       {object}                                        [extra]                 Extra data post to callback function
     * @returns     {boolean}                                                                       Is the request successfully sent
     * @see         AJAXRequest#post
     * @example
     * var ajax = new AJAXRequest();
     * ajax.postf("dataForm", function(obj) {
         *      alert(obj.responseText);
         * });
     */
    this.postf = function(formObj, callback, extra) {
        var p=[],vaf,pcbf,purl,pc,pm,ac=arguments.length,av=arguments;
        // check form legal
        formObj = formObj ? _VO(formObj) : false;
        if(!formObj || formObj.nodeName != "FORM") {
            return false;
        }
        // process form validate
        validfoo = formObj.getAttribute("onvalidate");
        validfoo = validfoo ? (typeof(validfoo)=="string" ? new Function(validfoo) : validfoo) : null;
        if(validfoo && !validfoo()) {
            return false;
        }
        // get url and method from formObj's attributes
        var url = formObj.getAttribute("action"), method = formObj.getAttribute("method");
        // check if content is empty
        var content = AJAX.formToStr(formObj);
        if(content.length == 0) {
            return false;
        }
        // send the request
        if(method.toUpperCase()=="POST") {
            return send(url, content, callback, "POST", true, extra);
        }
        else {
            url += (url.indexOf("?") > -1 ? "&" : "?") + content;
            return send(url, "", callback, "GET", true, extra);
        }
    }

    /**
     * Translate the form object to string
     *
     * @author      SurfChen <surfchen@gmail.com>
     * @link        http://www.surfchen.org/
     * @param       {object} formObject
     * @returns {string} The string of the form
     * @see         AJAXRequest#postf
     * @ignore
     */
    this.formToStr = function(formObj) {
        var qstr = "", and = "", elem, value;
        for(var i = 0; i< formObj.length; i+=1) {
            elem = formObj[i];
            if (elem.name!='') {
                value = undefined;
                switch(elem.type) {
                    case "select-one":
                        if(elem.selectedIndex > -1) {
                            value = elem.options[elem.selectedIndex].value;
                        }
                        else {
                            value = "";
                        }
                        break;
                    case "checkbox":
                    case "radio":
                        if (elem.checked == true) {
                            value = elem.value;
                        }
                        break;
                    default:
                        value = elem.value;
                }
                if(value != undefined) {
                    value = AJAX.encode(value);
                    qstr += and + elem.name + "=" + value;
                    and = "&";
                }
            }
        }
        return qstr;
    }
}